#include "synctest.h"

int synctest()
{
  int r;
  printf("TEST MECHANIZMOW SYNCHRONIZACYJNYCH\n");
  r = testPV();
  if (r != 0) return r;
  r = testPP();
  if (r != 0) return r;
  r = testTimeout();
  if (r != 0) return r;
  r = testPVMT();
  if (r != 0) return r;
  r = testWieleCzytelnikow();
  if (r != 0) return r;
  r = testWylacznosciPisarzy();
  if (r != 0) return r;
  r = testWylacznosciPisarzCzytelnik();
  if (r != 0) return r;
  r = testWylacznosciCzytelnikPisarz();
  if (r != 0) return r;
  r = testWarunku();
  if (r != 0) return r;
  r = testWarunkuTimeout();
  if (r != 0) return r;
  
  return 0;
}

int testPV()
{
  int fd, s, r;
  printf("Test P/V na semaforze o wartosci poczatkowej 1\n");
  fd = open(SEM1_NAME, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
  if (fd == -1)
  {
    printf("Blad przy tworzeniu pliku na semafor\n");
    return 1;
  }
  close(fd);
  s = _linda_sem_init(ftok(SEM1_NAME, 1), 1);
  if (s == -1)
  {
    printf("Blad przy tworzeniu semafora\n");
    unlink(SEM1_NAME);
    return 1;
  }
  if ((r = _linda_sem_P(s, 0)) != 0)
  {
    printf("Blad przy P (%d)\n", r);
    _linda_sem_destroy(s);
    unlink(SEM1_NAME);
    return 1;    
  }
  if (_linda_sem_V(s) == -1)
  {
    printf("Blad przy V\n");
    _linda_sem_destroy(s);
    unlink(SEM1_NAME);
    return 1;    
  }
  if ((r = _linda_sem_destroy(s)) != 0)
  {
    printf("Blad przy destroy (%d)\n", r);
    unlink(SEM1_NAME);
    return 1;    
  }
  unlink(SEM1_NAME);
  return 0;
}

int testPP()
{
  int fd, s, r;
  printf("Test P/P na semaforze o wartosci poczatkowej 2\n");
  fd = open(SEM1_NAME, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
  if (fd == -1)
  {
    printf("Blad przy tworzeniu pliku na semafor\n");
    return 1;
  }
  close(fd);
  s = _linda_sem_init(ftok(SEM1_NAME, 1), 2);
  if (s == -1)
  {
    printf("Blad przy tworzeniu semafora\n");
    unlink(SEM1_NAME);
    return 1;
  }
  if ((r = _linda_sem_P(s, 0)) != 0)
  {
    printf("Blad przy P1 (%d)\n", r);
    _linda_sem_destroy(s);
    unlink(SEM1_NAME);
    return 1;    
  }
if ((r = _linda_sem_P(s, 0)) != 0)
  {
    printf("Blad przy P2 (%d)\n", r);
    _linda_sem_destroy(s);
    unlink(SEM1_NAME);
    return 1;    
  }
  if ((r = _linda_sem_destroy(s)) != 0)
  {
    printf("Blad przy destroy (%d)\n", r);
    unlink(SEM1_NAME);
    return 1;    
  }
  unlink(SEM1_NAME);
  return 0;
}

int testTimeout()
{
  int fd, s, r;
  time_t tStart, tKon;
  printf("Test timeout na semaforze o wartosci poczatkowej 0\n");
  fd = open(SEM1_NAME, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
  if (fd == -1)
  {
    printf("Blad przy tworzeniu pliku na semafor\n");
    return 1;
  }
  close(fd);
  s = _linda_sem_init(ftok(SEM1_NAME, 1), 0);
  if (s == -1)
  {
    printf("Blad przy tworzeniu semafora\n");
    unlink(SEM1_NAME);
    return 1;
  }
  tStart = time(NULL);
  if ((r = _linda_sem_P(s, 1)) != 1)
  {
    printf("Blad przy P (%d)\n", r);
    _linda_sem_destroy(s);
    unlink(SEM1_NAME);
    return 1;    
  }
  tKon = time(NULL);
  if (tKon - tStart < 1)
  {
    printf("Blad: za krotko timeoutowano\n");
    _linda_sem_destroy(s);
    unlink(SEM1_NAME);
    return 1;
  }
  if ((r = _linda_sem_destroy(s)) != 0)
  {
    printf("Blad przy destroy (%d)\n", r);
    unlink(SEM1_NAME);
    return 1;    
  }
  unlink(SEM1_NAME);
  return 0;
}

int testPVMT()
{
  int fd, s, r;
  pthread_t pt;
  time_t tStart, tKon;
  printf("Test P/V na semaforze o wartosci poczatkowej 0, z wykorzystaniem dwoch uchwytow i dwoch watkow (program na chwile powinien stanac)\n");
  fd = open(SEM1_NAME, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
  if (fd == -1)
  {
    printf("Blad przy tworzeniu pliku na semafor\n");
    return 1;
  }
  close(fd);
  s = _linda_sem_init(ftok(SEM1_NAME, 1), 0);
  if (s == -1)
  {
    printf("Blad przy tworzeniu semafora\n");
    unlink(SEM1_NAME);
    return 1;
  }
  pthread_create(&pt, NULL, testPVMT_2, NULL);
  tStart = time(NULL);
  if ((r = _linda_sem_P(s, 0)) != 0)
  {
    printf("Blad przy P (%d)\n", r);
    _linda_sem_destroy(s);
    unlink(SEM1_NAME);
    return 1;    
  }
  tKon = time(NULL);
  if (tKon - tStart < 2)
  {
    printf("?: podejrzanie krotki czas oczekiwania na semafor\n");
  }
  pthread_join(pt, (void**) &fd);
  if ((r = _linda_sem_destroy(s)) != 0)
  {
    printf("Blad przy destroy (%d)\n", r);
    unlink(SEM1_NAME);
    return 1;    
  }
  unlink(SEM1_NAME);
  return 0;
}

void* testPVMT_2(void* a)
{
  int s;
  s = _linda_sem_get(ftok(SEM1_NAME, 1));
  if (s == -1)
  {
    printf("Blad przy pobieraniu semafora\n");
    unlink(SEM1_NAME);
    return NULL;
  }
  sleep(2);
  if (_linda_sem_V(s) == -1)
  {
    printf("Blad przy V\n");
    _linda_sem_destroy(s);
    unlink(SEM1_NAME);
    return NULL;    
  }
  return NULL;
}

int testWieleCzytelnikow()
{
  _linda_rw_lock rw, rw2, rw3;
  printf("Test wpuszczania wielu czytelnikow\n");
  if (_linda_create_rw_lock(RW1_NAME, &rw) != 0)
  {
    printf("Blad przy tworzeniu blokady R/W\n");
    return 1;
  }
  if (_linda_get_rw_lock(RW1_NAME, &rw2) != 0)
  {
    printf("Blad przy tworzeniu drugiego uchytu na blokade\n");
    _linda_destroy_rw_lock(&rw);
    return 1;
  }
  if (_linda_get_rw_lock(RW1_NAME, &rw3) != 0)
  {
    printf("Blad przy tworzeniu trzeciego uchytu na blokade\n");
    _linda_leave_rw_lock(&rw2);
    _linda_destroy_rw_lock(&rw);
    return 1;
  }
  _linda_lock_reader(&rw);
  _linda_lock_reader(&rw2);
  _linda_lock_reader(&rw3);
  _linda_release_reader(&rw);
  _linda_release_reader(&rw2);
  _linda_release_reader(&rw3);
  _linda_leave_rw_lock(&rw3);
  _linda_leave_rw_lock(&rw2);
  _linda_destroy_rw_lock(&rw);
  return 0;
}

int testWylacznosciPisarzy()
{
  _linda_rw_lock rw;
  pthread_t pt;
  int r;
  printf("Test wylacznosci dwoch pisarzy\n");
  if (_linda_create_rw_lock(RW1_NAME, &rw) != 0)
  {
    printf("Blad przy tworzeniu blokady R/W\n");
    return 1;
  }
  _linda_lock_writer(&rw);
  pthread_create(&pt, NULL, testWylacznosciPisarzy_2, NULL);
  sleep(2);
  _linda_release_writer(&rw);
  pthread_join(pt, (void**) &r);
  _linda_destroy_rw_lock(&rw);
  if (r == 1)
  {
    printf("Blad w watku pomocniczym - mozliwy brak blokady\n");
    return 1;    
  }
  return 0;
}

void* testWylacznosciPisarzy_2(void* a)
{
  _linda_rw_lock rw2;
  time_t tS, tK;
  if (_linda_get_rw_lock(RW1_NAME, &rw2) != 0)
  {
    printf("Blad przy tworzeniu drugiego uchytu na blokade\n");
    return (void*) 1;
  }
  tS = time(NULL);
  _linda_lock_writer(&rw2);
  tK = time(NULL);
  _linda_release_writer(&rw2);
  if (tK - tS < 2)
  {
    _linda_leave_rw_lock(&rw2);
    return (void*) 1;
  }
  _linda_leave_rw_lock(&rw2);
  return (void*) 0;
}

int testWylacznosciPisarzCzytelnik()
{
  _linda_rw_lock rw;
  pthread_t pt;
  int r;
  printf("Test wylacznosci pisarz->czytelnik\n");
  if (_linda_create_rw_lock(RW1_NAME, &rw) != 0)
  {
    printf("Blad przy tworzeniu blokady R/W\n");
    return 1;
  }
  _linda_lock_writer(&rw);
  pthread_create(&pt, NULL, testWylacznosciPisarzCzytelnik_2, NULL);
  sleep(2);
  _linda_release_writer(&rw);
  pthread_join(pt, (void**) &r);
  _linda_destroy_rw_lock(&rw);
  if (r == 1)
  {
    printf("Blad w watku pomocniczym - mozliwy brak blokady\n");
    return 1;    
  }
  return 0;
}

void* testWylacznosciPisarzCzytelnik_2(void* a)
{
  _linda_rw_lock rw2;
  time_t tS, tK;
  if (_linda_get_rw_lock(RW1_NAME, &rw2) != 0)
  {
    printf("Blad przy tworzeniu drugiego uchytu na blokade\n");
    return (void*) 1;
  }
  tS = time(NULL);
  _linda_lock_reader(&rw2);
  tK = time(NULL);
  _linda_release_reader(&rw2);
  if (tK - tS < 2)
  {
    _linda_leave_rw_lock(&rw2);
    return (void*) 1;
  }
  _linda_leave_rw_lock(&rw2);
  return (void*) 0;
}

int testWylacznosciCzytelnikPisarz()
{
  _linda_rw_lock rw;
  pthread_t pt;
  int r;
  printf("Test wylacznosci czytelnik->pisarz\n");
  if (_linda_create_rw_lock(RW1_NAME, &rw) != 0)
  {
    printf("Blad przy tworzeniu blokady R/W\n");
    return 1;
  }
  _linda_lock_reader(&rw);
  pthread_create(&pt, NULL, testWylacznosciCzytelnikPisarz_2, NULL);
  sleep(2);
  _linda_release_reader(&rw);
  pthread_join(pt, (void**) &r);
  _linda_destroy_rw_lock(&rw);
  if (r == 1)
  {
    printf("Blad w watku pomocniczym - mozliwy brak blokady\n");
    return 1;    
  }
  return 0;
}

void* testWylacznosciCzytelnikPisarz_2(void* a)
{
  _linda_rw_lock rw2;
  time_t tS, tK;
  if (_linda_get_rw_lock(RW1_NAME, &rw2) != 0)
  {
    printf("Blad przy tworzeniu drugiego uchytu na blokade\n");
    return (void*) 1;
  }
  tS = time(NULL);
  _linda_lock_writer(&rw2);
  tK = time(NULL);
  _linda_release_writer(&rw2);
  if (tK - tS < 2)
  {
    _linda_leave_rw_lock(&rw2);
    return (void*) 1;
  }
  _linda_leave_rw_lock(&rw2);
  return (void*) 0;
}

int testWarunku()
{
  _linda_rw_lock rw;
  _linda_rw_condition cond;
  pthread_t pt;
  int r, fd;
  printf("Test zmiennej warunkowej (chwile zajmuje)\n");
  fd = open(COND1_NAME, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
  if (fd == -1)
  {
    printf("Blad przy tworzeniu pliku na zmienna warunkowa\n");
    return 1;
  }
  close(fd);
  if (_linda_create_rw_lock(RW1_NAME, &rw) != 0)
  {
    printf("Blad przy tworzeniu blokady R/W\n");
    unlink(COND1_NAME);
    return 1;
  }
  if (_linda_create_rw_condition(&rw, COND1_NAME, &cond) != 0)
  {
    printf("Blad przy tworzeniu zmiennej warunkowej\n");
    unlink(COND1_NAME);
    _linda_destroy_rw_lock(&rw);
    return 1;
  }
  _linda_lock_writer(&rw);
  pthread_create(&pt, NULL, testWarunku_2, NULL);
  
  _linda_writer_wait_condition(&cond, 0);
  _linda_release_writer(&rw);
  pthread_join(pt, (void**) &r);
  _linda_destroy_rw_lock(&rw);
  if (r == 1)
  {
    printf("Blad w watku pomocniczym - mozliwy brak blokady\n");
    return 1;    
  }
  unlink(COND1_NAME);
  return 0;
}

void* testWarunku_2(void* a)
{
  _linda_rw_lock rw2;
  _linda_rw_condition cond2;
  if (_linda_get_rw_lock(RW1_NAME, &rw2) != 0)
  {
    printf("Blad przy tworzeniu drugiego uchytu na blokade\n");
    return (void*) 1;
  }
  
  if (_linda_get_rw_condition(&rw2, COND1_NAME, &cond2) != 0)
  {
    printf("Blad przy tworzeniu drugiego uchytu na blokade\n");
    return (void*) 1;
  }
  
  sleep(2);
  _linda_lock_writer(&rw2);
  _linda_writer_signal_condition(&cond2);
  _linda_release_writer(&rw2);
  _linda_leave_rw_lock(&rw2);
  return (void*) 0;
}

int testWarunkuTimeout()
{
  _linda_rw_lock rw;
  _linda_rw_condition cond;
  int fd;
  printf("Test timeoutu zmiennej warunkowej\n");
  fd = open(COND1_NAME, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
  if (fd == -1)
  {
    printf("Blad przy tworzeniu pliku na zmienna warunkowa\n");
    return 1;
  }
  close(fd);
  if (_linda_create_rw_lock(RW1_NAME, &rw) != 0)
  {
    printf("Blad przy tworzeniu blokady R/W\n");
    unlink(COND1_NAME);
    return 1;
  }
  if (_linda_create_rw_condition(&rw, COND1_NAME, &cond) != 0)
  {
    printf("Blad przy tworzeniu zmiennej warunkowej\n");
    unlink(COND1_NAME);
    _linda_destroy_rw_lock(&rw);
    return 1;
  }  _linda_lock_writer(&rw);
  _linda_writer_wait_condition(&cond, 3);
  _linda_release_writer(&rw);
  _linda_destroy_rw_lock(&rw);

  unlink(COND1_NAME);
  return 0;
}